/**
 * 
 */
package com.miolr.core.service.local;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.miolr.core.dao.GenericDao;
import com.miolr.core.model.Persistable;
import com.miolr.core.query.PageModel;
import com.miolr.core.query.Parameter;
import com.miolr.core.service.GenericService;

/**
 * @author iday
 * 
 */
public class LocalGenericService<D extends GenericDao<T>, T extends Persistable<?>>
		implements GenericService<D, T> {

	protected D dao;

	@Autowired
	public void setDao(D dao) {
		this.dao = dao;
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public List<T> queryAll(DetachedCriteria detachedCriteria, PageModel pm,
			boolean cached, String cacheRegion) {
		for (Entry<String, Object> entry : pm.getParams().entrySet()) {
			Object value = entry.getValue();
			String key = entry.getKey();
			if (value instanceof Parameter) {
				Parameter param = (Parameter) value;
				switch (param.type()) {
				case EQUAL:
					detachedCriteria.add(Restrictions.eq(key, param.value()));
					break;
				case NOTEQUAL:
					detachedCriteria.add(Restrictions.ne(key, param.value()));
					break;
				case LIKE:
					detachedCriteria.add(Restrictions.like(key, param.value()));
					break;
				case CONTAIN:
					detachedCriteria.add(Restrictions.like(key,
							(String) param.value(), MatchMode.ANYWHERE));
					break;
				case GREATTHEN:
					detachedCriteria.add(Restrictions.gt(key, param.value()));
					break;
				case LESSTHEN:
					detachedCriteria.add(Restrictions.lt(key, param.value()));
					break;
				case GREATEQUAL:
					detachedCriteria.add(Restrictions.ge(key, param.value()));
					break;
				case LESSEQUAL:
					detachedCriteria.add(Restrictions.le(key, param.value()));
					break;
				case ISNULL:
					detachedCriteria.add(Restrictions.isNull(key));
					break;
				case IN:
					detachedCriteria.add(Restrictions.in(key,
							(Object[]) param.value()));
					break;
				default:
					break;
				}
			} else {
				detachedCriteria.add(Restrictions.eq(key, value));
			}
		}
		return dao.queryAll(detachedCriteria, pm, cached, cacheRegion);
	}
	
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public List<T> queryAll(DetachedCriteria detachedCriteria, PageModel pm,
			boolean cached) {
		return this.queryAll(detachedCriteria, pm, cached, null);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public List<T> queryAll(PageModel pm, boolean cached, String cacheRegion) {
		return dao.queryAll(pm, cached, cacheRegion);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public List<T> queryAll(String sql, PageModel pm) {
		return dao.queryAll(sql, pm);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public List<T> queryAll(String sql, PageModel pm, boolean cached) {
		return dao.queryAll(sql, pm, cached);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public List<T> queryAll(String sql) {
		return dao.queryAll(sql);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public List<T> queryAll(String sql, boolean cached) {
		return dao.queryAll(sql, cached);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public List<T> findAll() {
		return dao.findAll();
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public T findByPrimaryKey(Serializable key) {
		return dao.findByPrimaryKey(key);
	}
	
	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public T findByPrimaryKey(Serializable key, String...nestedNames) {
		T t = dao.findByPrimaryKey(key);
		this.dao.initLazeProperties(t, nestedNames);
		return t;
	}
	
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public T loadByPrimaryKey(Serializable key, String...nestedNames) {
		return this.findByPrimaryKey(key, nestedNames);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void deleteAll(Collection<T> c) {
		dao.deleteAll(c);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void delete(T obj) {
		dao.delete(obj);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void deleteByPrimaryKey(Serializable key) {
		dao.deleteByPrimaryKey(key);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void saveOrUpdate(Collection<T> c) {
		dao.saveOrUpdate(c);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void merge(T t) {
		dao.merge(t);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void update(T obj) {
		dao.update(obj);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void update(T obj, String[] ignored) {
		T t = this.findByPrimaryKey(obj.getId());
		BeanUtils.copyProperties(obj, t, ignored);
		this.update(t);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void save(T obj) {
		dao.save(obj);
	}

}
